if SERVER then
	
	local function setOwner(ply, prop)
		if ply && prop && (not prop:IsPlayer()) then
			if prop:GetNetworkedInt("moocow_owner") then
				umsg.Start("moocow_propprotect_updateowner", player.GetAll())
					umsg.Long(prop:EntIndex())
					umsg.Long(ply:EntIndex())
				umsg.End()
			end
			prop:SetNetworkedInt("moocow_owner", ply:EntIndex()) //Update on the network either way - for players who join later
		end
	end
	
	local allProps = {}
	
	local function loadWorldProps()
		local settings = getMOOSettings()
		if settings["prop_protect_loadworld"] == 1 then
			for i, prop in pairs(ents.GetAll()) do
				prop:SetNetworkedInt("moocow_owner", -1)
				allProps[-1] = prop:EntIndex()
			end
		end
	end
	hook.Add("InitPostEntity", "moocow_propprotect_worldprotect", loadWorldProps)
	
	local function propWasSpawned(ply, model, prop)
		setOwner(ply, prop)
		local index = ply:EntIndex()
		if not allProps[index] then
			allProps[index] = {}
		end
		table.insert(allProps[ply:EntIndex()], prop:EntIndex())
	end
	
	local function otherWasSpawned(ply, prop)
		setOwner(ply, prop)
		local index = ply:EntIndex()
		if not allProps[index] then
			allProps[index] = {}
		end
		table.insert(allProps[ply:EntIndex()], prop:EntIndex())
	end
	
	registerCommand("prop_cleanup", "Cleanup the props of the specified player", function(param)
		local cleanupMsg = "caller cleaned up target's props"
		if param["settings"]["cleanup_msg"] then
			cleanupMsg = param["settings"]["cleanup_msg"]
		end
		local cleanupTable = {}
		cleanupTable["caller"] = getCaller(param[1])
		local targs = string.Explode(" ", param[3])
		for i, targ in pairs(targs) do
			local plyTarg = getPlayerByName(targ)
			if plyTarg then
				cleanupTable["target"] = plyTarg:GetName()
				display(param[1], "prop_cleanup", { plyTarg } , parseDisplayMessage(cleanupMsg, cleanupTable))
				
				local toClean = allProps[plyTarg:EntIndex()]
				if toClean then
					for x, prop in pairs(toClean) do
						ents.GetByIndex(prop):Remove()
						table.remove(toClean, x)
					end
					table.Empty(toClean)
				end
			elseif targ == "server" then
				cleanupTable["target"] = "Server"
				display(param[1], "prop_cleanup", { plyTarg } , parseDisplayMessage(cleanupMsg, cleanupTable))
				
				local toClean = allProps[-1]
				if toClean then
					for x, prop in pairs(toClean) do
						ents.GetByIndex(prop):Remove()
						table.remove(toClean, x)
					end
					table.Empty(toClean)
				end
			end
		end
	end)
	
/*=====================================================================
PLAYER ADMINISTRATION PERMISSIONS SYSTEMS
=====================================================================*/	

	local rankedPlayers = {}
	
	registerCommand("PROPPROTECT_ADMIN", "Grants players with access to this plugin the ability to move all props", function(param)
		/*
			An empty plugin for two reasons:
				1. This plugin is a flag - it has no function other then to let it be known that a group has it
				2. This function may not be called - it has a capital lettered name and all input is converted to lowercase
		*/
	end)
	
	local function getIsPropAdmin(plyid)
		local ply = player.GetByID(plyid)
		if ply then
			return rankHasPlugin(getRank(ply), "PROPPROTECT_ADMIN")
		end
	end
	
	local function setRankOnJoin(ply)
		rankedPlayers[ply:EntIndex()] = getRank(ply) //Function in moocow_server.lua
	end
	hook.Add("PlayerInitialSpawn", "moocow_propprotect_initspawn", setRankOnJoin)
	
	local function setRankIfDelta(ply) //Used to check for team changes
		local id = ply:EntIndex()
		local rank = getRank(ply)
		if rank != rankedPlayers[id] then
			rankedPlayers[id] = rank
		end
	end
	hook.Add("PlayerSpawn", "moocow_propprotect_plyspawn", setRankIfDelta)
	
/*=====================================================================
FRIEND  SYSTEMS
=====================================================================*/	

	local friends = {}

	local function updateFriend(caller, friend)
		local callid = caller:EntIndex()
		local frieid = friend:EntIndex()
		if not friends[callid] then
			friends[callid] = {}
		end
		local isFriend = friends[callid][frieid]
		if not isFriend || isFriend == -1 then
			friends[callid][frieid] = 1
			return 1
		else
			friends[callid][frieid] = -1
			return -1
		end
	end

	require("datastream")
	function GAMEMODE:AcceptSteam(pl, handler, id)
		return true //Allow streams
	end
	
	local function changeInFriends(data)
		local procdata = data:GetDecodedData()
		local from = player.GetByID(table.remove(procdata, 1))
		if from then
			for i = 1, table.Count(procdata) do
				local to = player.GetByID(table.remove(procdata, 1))
				if to then
					updateFriend(from, to)
				end
			end
		end
	end
	datastream.Hook("moocow_changInFriends", changeInFriends)
	
	registerCommand("prop_friend", "Adds the specified player as a prop friend", function(param)
		if not param[1]:IsValid() then
			return nil
		end
		
		local friendOn = "caller added target as a prop friend"
		if param["settings"]["propfriend_msg_on"] then
			friendOn = param["settings"]["propfriend_msg_on"]
		end
		local friendOff = "caller removed target as a prop friend"
		if param["settings"]["propfriend_msg_off"] then
			friendOff = param["settings"]["propfriend_msg_off"]
		end
		local friendTable = {}
		friendTable["caller"] = param[1]:GetName()
		local targs = string.Explode(" ", param[3])
		for i, targ in pairs(targs) do
			local plyTarg = getPlayerByName(targ)
			if plyTarg then
				friendTable["target"] = plyTarg:GetName()
				local change = updateFriend(param[1], plyTarg)
				if change then
					if change == 1 then
						display(param[1], "prop_friend", { plyTarg } , parseDisplayMessage(friendOn, friendTable))
					else
						display(param[1], "prop_friend", { plyTarg } , parseDisplayMessage(friendOff, friendTable))
					end
				end
			end
		end
	end)

/*=====================================================================
TOOLGUN (EXCEPTION) SYSTEMS
=====================================================================*/	

local toolexceptions = {}

local function loadToolExcepts()
	toolexceptions = {}
	if not file.Exists("moocow/propprotect_tools") then
		file.Write("moocow/propprotect_tools", "")
	end
	local mess = file.Read("moocow/propprotect_tools")
	local lines = string.Explode("\n", mess)
	for i, t in pairs(lines) do
		toolexceptions[t] = 1
	end
end

local function writeToolExcepts()
	local build = ""
	for i, t in pairs(toolexceptions) do
		build = build .. "\n" .. i
	end
	file.Write("moocow/propprotect_tools.txt", build)
end

local function tblRemoveByStr(arr, str)
	local build = {}
	for i, x in pairs(arr) do
		if i != str then
			build[i] = x
		end
	end
	return build
end

registerCommand("prop_toolexcep", "Adds the specified tool to the allowed list", function(param)
	local toolOn = "caller added tool as an exception"
	if param["settings"]["toolexcep_msg_on"] then
		toolOn = param["settings"]["toolexcep_msg_on"]
	end
	local toolOff = "caller removed tool as an exception"
	if param["settings"]["toolexcep_msg_off"] then
		toolOff = param["settings"]["toolexcep_msg_off"]
	end
	local toolTable = {}
	toolTable ["caller"] = getCaller(param[1])
	local targs = string.Explode(" ", param[3])
	for i, targ in pairs(targs) do
		if string.Trim(targ) != "" then
			toolTable["tool"] = targ
			local has = toolexceptions[targ]
			if has == 1 then
				toolexceptions = tblRemoveByStr(toolexceptions, targ)
				display(param[1], "prop_toolexcep", { nil } , parseDisplayMessage(toolOff, toolTable ))
			else
				toolexceptions[targ] = 1
				display(param[1], "prop_toolexcep", { nil } , parseDisplayMessage(toolOn , toolTable ))
			end
		end
	end
	writeToolExcepts()
end)

/*=====================================================================
USAGE SYSTEMS
=====================================================================*/	

	local function canAlterProp(plyid, owner)
		if plyid && owner then
			if plyid == owner || (friends[owner] && friends[owner][plyid] == 1) || getIsPropAdmin(plyid) then
				return true
			end
		end
		return false
	end

	local function attemptToPhysUnfreeze(weapon, ply)
		local plyid = ply:EntIndex()
		local prop = ply:GetEyeTrace().Entity
		
		if prop then
			local owner = prop:GetNetworkedInt("moocow_owner")
			if not owner then
				setOwner(ply, prop)
				return true
			end
			if not canAlterProp(plyid, owner) then
				return true
			end
		else
			return true
		end
	end
	
	local function attemptToPhysFreeze(weapon, phys, prop, ply)
		local plyid = ply:EntIndex()
		local owner = prop:GetNetworkedInt("moocow_owner")
		if not owner then
			setOwner(ply, prop)
			return true
		end
		if not canAlterProp(plyid, owner) then
			return true
		end
	end
	
	local canPhysPlayers = -1
	
	local function attemptToPhysGun(ply, prop)
		local owner = prop:GetNetworkedInt("moocow_owner")
		if prop:IsPlayer() && canPhysPlayers != 1 then
			return false
		end
		if owner && owner != 0 then
				return canAlterProp(ply:EntIndex(), owner)
		else
			setOwner(ply, prop)
			return true
		end
	end
	
	local function attemptToDamage(prop, inflictor, attacker, amount, dmginfo)
		if not prop.IsPlayer() then
			local owner = prop:GetNetworkedInt("moocow_owner")
			if owner || (not attacker) || (not attacker.IsPlayer()) || (not canAlterProp(attacker:EntIndex(), owner)) then
				prop:SetHealth(prop:Health() + amount)
			elseif not owner then
				setOwner(attacker, prop)
			end
		end
	end
	
	local function attemptToTool(ply, tr, usedTool)
		local plyid = ply:EntIndex()
		local prop = tr.Entity
		
		if prop then
			if toolexceptions[usedTool] then
				return true
			end
			local owner = prop:GetNetworkedInt("moocow_owner")
			if not owner || owner == 1 then
				setOwner(ply, prop)
				return true
			elseif canAlterProp(plyid, owner) then
				return true
			end
		end
		return false
	end
	
	local propstatus = 1
	
	local function addPropProtectHooks()
		local settings = getMOOSettings()
		local pp_props = settings["prop_protect_props"]
		if (not pp_props) || pp_props == 1 then
			hook.Add("PlayerSpawnedProp", "moocow_propprotect_asignfirstownership_prop", propWasSpawned)
		end
		local pp_npcs = settings["prop_protect_npcs"]
		if (not pp_npcs) || pp_npcs == 1 then
			hook.Add("PlayerSpawnedNPC", "moocow_propprotect_asignfirstownership_npc", otherWasSpawned)
		end
		local pp_sents = settings["prop_protect_sents"]
		if (not pp_sents) || pp_sents == 1 then
			hook.Add("PlayerSpawnedSENT", "moocow_propprotect_asignfirstownership_sent", otherWasSpawned)
		end
		local pp_vehs = settings["prop_protect_vehicles"]
		if (not pp_vehs) || pp_vehs == 1 then
			hook.Add("PlayerSpawnedVehicle", "moocow_propprotect_asignfirstownership_veh", otherWasSpawned)
		end
		local pp_effs = settings["prop_protect_effects"]
		if (not pp_effs) || pp_effs == 1 then
			hook.Add("PlayerSpawnedEffect", "moocow_propprotect_asignfirstownership_eff", otherWasSpawned)
		end
		local pp_rags = settings["prop_protect_ragdolls"]
		if (not pp_rags) || pp_rags == 1 then
			hook.Add("PlayerSpawnedRagdoll", "moocow_propprotect_asignfirstownership_rag", otherWasSpawned)
		end
		local pp_freeze = settings["prop_protect_freezing"]
		if (not pp_freeze) || pp_freeze == 1 then
			hook.Add("OnPhysgunReload", "moocow_propprotect_physreload", attemptToPhysUnfreeze)
			hook.Add("OnPhysgunFreeze", "moocow_propprotect_physfreeze", attemptToPhysFreeze)
		end
		local pp_pickup = settings["prop_protect_physgun"]
		if (not pp_pickup) || pp_pickup == 1 then
			hook.Add("PhysgunPickup", "moocow_propprotect_physpickup", attemptToPhysGun)
		end
		local pp_grav = settings["prop_protect_gravpickup"]
		if (not pp_grav) || pp_grav == 1 then
			hook.Add("GravGunPickupAllowed", "moocow_propprotect_gravpickup", attemptToPhysGun)
		end
		local pp_punt = settings["prop_protect_gravpunt"]
		if (not pp_punt) || pp_punt == 1 then
			hook.Add("GravGunPunt", "moocow_propprotect_gravpunt", attemptToPhysGun)
		end
		local pp_damage = settings["prop_protect_damage"]
		if (not pp_damage) || pp_damage == 1 then
			hook.Add("EntityTakeDamage", "moocow_propprotect_enttakedamage", attemptToDamage)
		end
		local pp_tool = settings["prop_protect_toolgun"]
		if (not pp_tool) || pp_tool== 1 then
			hook.Add("CanTool", "moocow_propprotect_cantool", attemptToTool)
		end
		local setPhysPlayers = settings["prop_protect_physplayers"]
		if setPhysPlayers then
			canPhysPlayers = setPhysPlayers
		end
	end
	addPropProtectHooks()
	
	local function remPropProtectHooks()
		hook.Remove("OnPhysgunReload", "moocow_propprotect_physreload")
		hook.Remove("OnPhysgunFreeze", "moocow_propprotect_physfreeze")
		hook.Remove("PhysgunPickup", "moocow_propprotect_physpickup")
		hook.Remove("GravGunPickupAllowed", "moocow_propprotect_gravpickup")
		hook.Remove("GravGunPunt", "moocow_propprotect_gravpunt")
		hook.Remove("EntityTakeDamage", "moocow_propprotect_enttakedamage")
		hook.Remove("CanTool", "moocow_propprotect_cantool")
	end
	
	registerCommand("propprotect_activate", "Turns on/off prop protect", function(param)
		local propactOn = "caller turned prop protection on"
		if param["settings"]["propactivate_msg_on"] then
			propactOn = param["settings"]["propactivate_msg_on"]
		end
		local propactOff = "caller turned prop protection off"
		if param["settings"]["propactivate_msg_off"] then
			propactOff = param["settings"]["propactivate_msg_off"]
		end
		local propactTable = {}
		propactTable["caller"] = getCaller(param[1])
		if propstatus == -1 then
			propstatus = 1
			addPropProtectHooks()
			display(param[1], "propprotect_activate", { nil } , parseDisplayMessage(propactOn, propactTable))
		else
			propstatus = -1
			remPropProtectHooks()
			display(param[1], "propprotect_activate", { nil } , parseDisplayMessage(propactOff, propactTable))
		end
	end)
	
else //=====================================================CLIENT=======================================================
	local owners = {}

	local function updateOwner(data) //The one means to override the table that's set as data is collected normally
		owners[data:ReadLong()] = data:ReadLong()
	end
	usermessage.Hook("moocow_propprotect_updateowner", updateOwner)
	
	local function getOwner(prop)
		local index = prop:EntIndex()
		local lOwner = owners[index]
		if lOwner then
			return lOwner
		else
			local nOwner = prop:GetNetworkedInt("moocow_owner")
			owners[index] = nOwner //Remeber - if it's null, it will run again
			return nOwner //As indicated above, this may return null
		end
	end
end
